package cn.good.yan.config;

import cn.good.yan.config.service.UserDetailsServiceImpl;
import cn.good.yan.handle.MyAccessDenHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * Security配置类
 *
 * @author shengren.yan
 * @create 2024/9/15
 */
@Configuration
//// Spring项目总需要添加此注解，SpringBoot项目中不需要该注解
//@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    // 自定义：403处理方式
    @Autowired
    private MyAccessDenHandler myAccessDenHandler;

    // 自定义登录逻辑
    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    // 数据源
    @Autowired
    private DataSource dataSource;
    // 忘记密码
    @Autowired
    private PersistentTokenRepository persistentTokenRepository;


    //  【基于内存的用户认证】 方式1:
    //  1.演示：基于内存的用户认证  【只有基于内存的用户认证 】 进行开启
    //  配置该类，启动程序，登录密码，是所设置的用户名yan，不再是 Security 默认提供的了
    //  UserDetailsService 用来管理用户信息
    //  访问：http://localhost:8081/user/ok  ，用没有登录，跳转到登录页面
    //  输入：用户名和密码 （yan）（123456）进行登录，登录成功，得到user/ok的请求结果
    //  输入错误的用户名和密码，登录失败，没有权限得到user/ok的请求结果
    //  (注意：密码不能加密)
//    @Bean
//    public UserDetailsService userDetailsService() {
//        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
//        manager.createUser(
//                User
//                        .withDefaultPasswordEncoder()
//                        .username("yan")    // 自定义用户名
//                        .password("123456") // 自定义密码
//                        .roles("USER")      // 自定义角色
//                        .build()
//        );
//        return manager;
//    }

    // 定义密码加密规则
    @Bean
    public PasswordEncoder getPw() {
        return new BCryptPasswordEncoder();
    }


    // 自定义登录页面
    // 演示：访问：http://localhost:8081/main.html
    // 输入正确密码，后跳转到 /toIndex 这个请求，该请求在 LoginController类中定义
    // login.html 是自己定义的登录页面
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()
                // 该请求不需要被认证
                .antMatchers("/login.html").permitAll()
                .antMatchers("/erro.html").permitAll()
//                .antMatchers("/js/**", "/css/**", "/images/**").permitAll()
//                // 放行所有的以.png为后缀的请求
//                .regexMatchers(".+[.]png").permitAll()
//                // 放行只有Get请求的/ok的请求
//                .regexMatchers(HttpMethod.GET, "/ok").permitAll()
//                // 跟antMatchers一样，只是多了 servletPath，这个配置在yml里配置 spring.mvc.servlet.path: xxx，需要配置
//                .mvcMatchers("/ok").servletPath("xxx").permitAll()
//                // 【权限、角色判断】
//                // 只有admin,权限1权限的，才可以访问该/ok请求, 权限在UserDetailsServiceImpl里配置
//                .antMatchers("/ok").hasAuthority("admin,权限1")
//                // 只有角色1,角色2 角色的，才可以访问该/ok请求, 权限在UserDetailsServiceImpl里配置
//                .antMatchers("/ok").hasAnyRole("角色1,角色2")
                // 必须是IP：127.0.0.1请求来，才能访问到ok请求，其他都是403
//                .antMatchers("/ok").hasIpAddress("127.0.0.1")
                // 所有请求都必须认证，登录才能访问
                .anyRequest().authenticated();

        // 表单提交
        http.formLogin()
                // 处理登录post请求接口，无需自己实现
                .loginProcessingUrl("/login")
                // 自定义登录页面,默认在static下
                .loginPage("/login.html")
                // 自定义表单用户名参数，默认是username
                .usernameParameter("username")
                // 自定义表单密码参数，默认是password
                .passwordParameter("password")
                // 登录成功后，跳转的请求
                .successForwardUrl("/toIndex")
                // 登录成功后，跳转的请求 - 自定义的，调整到百度去，注意不能与successForwardUrl共存
//                .successHandler(new MyAuthenticatHandler("https://www.baidu.com/"))

                // 登录失败后，跳转的请求
//                .failureForwardUrl("/toError")
                // 登录失败后，跳转的请求 - 自定义的，调整到其他页面去，注意不能与failureForwardUrl共存
//                .failureHandler(new MyAuthenticaFailtHandler("/erro.html"))

        ;

        http.csrf(csrf -> csrf.disable());  // 暂时关闭csrf攻击 - csrf是跨站请求伪造

        // 异常处理 - 403的异常处理
        http.exceptionHandling().accessDeniedHandler(myAccessDenHandler);

        // 记住密码
        http.rememberMe()
                // 失效时间，单位秒
                .tokenValiditySeconds(60)
                // 自定义登录逻辑
                .userDetailsService(userDetailsService)
                // 持久层对象
                .tokenRepository(persistentTokenRepository);

        // 退出登录
        http.logout()
                .logoutUrl("/logout")   // 自定义登出api，无需自己实现
                .logoutSuccessUrl("/login.html"); // 退出登录跳转页面

    }


    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        // 自动创建表，第一次启动，需要启动，自动把表创建好，第二次启动，不需要开启，注释掉
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;

    }


}
